_package("alz.core");

_import("alz.util.Base64");

/**
 * 请求对象
 */
_class("Request", "", function(){
	var SPECIAL_PARAMS = {
		"#header"  : ["_headers" ],
		"#mimetype": ["_mimetype"]
	};
	var MIME_TYPE = {
		"gif": "image/gif",
		"png": "image/png",
		"jpg": "image/jpeg"
	};
	this._init = function(method, url, params, type, agent, func){
		_super._init.call(this);
		this._data = {         //原始请求数据，只读
			"method": method,
			"url"   : url,
			"params": params,  //原始的请求参数
			"type"  : type,
			"agent" : agent,
			"func"  : func
		};
		this._app = null;      //请求所属应用(发送请求的应用)
		this._status = "";     //状态标志(abort=已终止,retry=重试中)
		this._tid   = -1;    //回调任务id
		this._timer  = 0;     //控制超时的定时器
		this._mode   = "";    //请求方式(xhr|script|iframe|storage)
		this._url    = url;   //计算出来的请求地址
		this._query  = "";    //格式化后的请求参数字符串
		//for xhr
		this._http   = null;  //XHR组件
		this._headers = null; //预设请求头
		this._mimetype = "";  //预设mimetype
		//for script
		this._script = null;  //script元素
	};
	this.init = function(app){
		this._app = app;
		this._tid = runtime._task.add(this._data.agent, this._data.func);
		runtime._task.addDependence(this._tid, app, "beforeCallback");
		app.fireEvent({"type": "beforeNetInvoke", "req": this});
	};
	this.dispose = function(){
		this._script = null;
		this._headers = null;
		this._http = null;
		this._app = null;
		this._data.agent = null;
		this._data.func = null;
		this._data.params = null;
		this._data = null;
		_super.dispose.apply(this);
	};
	this.beforeInvoke = function(){
		//if(!/^https:\/\//.test(this._url)){  //https协议，不使用代理跨域方案
		//手机真实环境下，再合并参数之前，包装url，实现跨域目的
		if(/^https?:\/\//.test(this._url)){  //相对地址不进行任何包装处理
			if(!CloudAPI.__debug__ && CloudAPI.isMobile() && this._app._useProxy){
				this._url = CloudAPI.Utility.wrapAjaxUrl(this._url);
			}
		}
		//}else{
		//	//this._url = this._url.replace(/^https:\/\//, "http://");
		//}
	};
	/*
	this.getApp = function(){
		return this._app;
	};
	this.setApp = function(v){
		this._app = v;
	};
	*/
	this.setMode = function(v){
		this._mode = v;
	};
	/**
	 * 把一个json对象转换成按http通讯协议要求进行编码的字符串
	 * @param {Object} json 请求参数对象
	 * @return {String} 编码后的字符串
	 */
	this._json2str = function(json){
		var sb = [];
		for(var k in json){
			if(k.charAt(0) == "&") continue;  //忽略隐藏参数
			//下面的改进，支持跨window的json对象传递，改进逻辑参考clone方法实现
			switch(typeof json[k]){
			//case "undefined": break;  //目前不支持undefined值
			case "boolean":
			case "number":
			case "string":
				sb.push(k + "=" + encodeURIComponent(json[k]));
				break;
			case "object":
				if(json[k] === null){  //null
					//目前不支持null值
				}else{
					//因为跨 window 操作，所以不能使用 instanceof 运算符
					//if(json[k] instanceof Array){
					if("length" in json[k]){  //array
						//把js数组转换成PHP能够接收的PHP数组
						for(var i = 0, len = json[k].length; i < len; i++){
							sb.push(k + "=" + encodeURIComponent(json[k][i]));
						}
					}else{  //object
						//目前不支持object类型的参数
					}
				}
				break;
			}
		}
		return sb.join("&");
	};
	/**
	 * 预处理特殊参数
	 */
	this.preprocessSpecial = function(){
		var params = this._data.params;
		if(typeof params != "string"){
			for(var key in SPECIAL_PARAMS){
				if(key in params){
					this[SPECIAL_PARAMS[key]] = params[key];
					delete params[key];
				}else if(key == "#mimetype"){
					this._mimetype = this._data.type == "xml" ? "text/xml" : "";
				}
			}
		}
	};
	this._processQuery = function(){
		if(typeof this._data.params != "string"){  //"object"
			this._query = this._json2str(this._data.params);
		}
		var flag = this._url != "";
		this._url = this._data.url;
		if(flag){  //[TODO]retry时的跨域问题解决办法不好，导致_processQuery不够通用
			this.beforeInvoke();
		}
		/*
		if(this._url == ""){  //[TODO]附加的参数有问题
			this._url = this._data.url;
		}
		*/
		if(this._data.method == "GET" && this._query != ""){
			var url = this._data.url;
			var ch = url.charAt(url.length - 1);
			if(ch != "?" && ch != "&"){
				this._url += (url.indexOf("?") == -1 ? "?" : "&");
			}
			this._url += this._query;
			this._query = "";
		}
	};
	/**
	 * 预处理请求参数，主要针对GET方式生成拼合的URL
	 */
	this.preprocess = function(){
		this._processQuery();
		switch(this._mode){
		case "store":
			if(!this._app._debug){
				this._url = this._url.replace(/\/citylife\/([a-z]+)\/([a-z]+)(\?|$)/i, "/caf/build/server.php?_c=$1&_a=$2&");
			}
			break;
		case "script":
			break;
		case "xhr":
			//PC开发模式下或手机iframe调试模式下，再合并参数之后，使用php代理包装url，模拟跨域请求，实现跨域目的
			//手机环境下iframe情况
			if(/^https?:\/\//.test(this._url)){  //相对地址不进行任何包装处理
				if((CloudAPI.__debug__ || !CloudAPI.isMobile()) && this._app._useProxy){
					this._url = CloudAPI.wrapProxy(this._url);
				}
			}
			break;
		}
	};
	this.getAllHeaders = function(){
		var headers = {};
		var arr = this._http.getAllResponseHeaders().split(/\r?\n/);
		for(var i = 0, len = arr.length; i < len; i++){
			var row = arr[i];
			if(row === "") continue;
			var p = row.indexOf(": ");
			if(p != -1){
				var k = row.substr(0, p).toLowerCase();
				var v = row.substr(p + 2);
				headers[k] = v;
			}
		}
		return headers;
	};
	/**
	 * 获取请求的返回数据
	 * @return {String|Object|XMLDocument|BinaryString}
	 */
	this._getData = function(){
		var data, xhr = this._http;
		if(this._data.type != "base64"){
			var text = xhr.responseText;
			runtime.log("responseText:" + text.substr(0, 512));
			//检查并忽略PHP输出的warning信息
			if(text.indexOf("<br />") != -1 && text.indexOf("<b>Warning</b>") != -1){
				runtime.warning("PHP返回数据含有warning信息");
				text = text.substr(text.lastIndexOf("<br />") + 6);
			}
			switch(this._data.type){
			case "text":
				data = text;
				break;
			case "json":
				//[TODO]数据格式错误的情况下，需要返回原始的真实数据，以便具体业务逻辑进行错误处理
				data = runtime.parseJson(text);  //eval("(" + text + ")")
				break;
			case "xml":
				if(runtime.ie){  //typeof ActiveXObject != "undefined"
					var xmlDoc = new ActiveXObject("Msxml.DOMDocument");
					xmlDoc.async = false;
					xmlDoc.loadXML(text);
					data = xmlDoc;  //.documentElement
				}else{
					data = xhr.responseXML;
				}
				break;
			}
		}else{  //"base64":  //二进制字符串
			var bin = true, response, str;
			if("mozResponse" in xhr){
				response = xhr.mozResponse;
			}else if(xhr.mozResponseArrayBuffer){
				response = xhr.mozResponseArrayBuffer;
			}else if("responseType" in xhr){
				response = xhr.response;
			}else{
				response = xhr.responseText;
				bin = false;
			}
			if(response){  //如果请求失败response可能是null值
				if(bin && typeof Uint8Array != "undefined"){  //支持二进制操作
					var len = response.byteLength;
					str = {
						"arr": new Uint8Array(response, 0, len),
						"length": len,
						"charCodeAt": function(i){
							return this.arr[i];
						}
					};
				}else{
					//str = response;
					str = {
						"str": response,
						"length": response.length,
						"charCodeAt": function(i){
							var n = this.str.charCodeAt(i);
							if(n > 0xFF){
								n = n & 0xFF;
							}
							return n;
						}
					};
				}
				data = {
					"mimetype": MIME_TYPE[this._data.params["&ext"]],
					"data": Base64.encode(str)
				};
			}else{
				data = {
					"mimetype": MIME_TYPE[this._data.params["&ext"]],
					"data": Base64.encode("")
				};
			}
		}
		return data;
	};
	this.onReady = function(){
		if(this._http.readyState != 4) return;
		if(this._status == "abort") return;
		runtime.stopTimer(this._timer);
		if(this._http.status == 200 || this._http.status == 0){
			var task = runtime._task._hash[this._tid];
			var arr = [];
			if(task.func && task.func.length >= 2){
				task.args = this.getAllHeaders();
			}
			this.send(this._getData());
		}else{
			runtime.error("[Request::onReady]callback error(status=" + this._http.status + ")");
			runtime.error("[Request::onReady]#" + this._data.url);
			//runtime._task.cancel(this._tid);
			this.send({
				"code": -1000,
				"msg" : "xhr invoke error",
				"data": null
			});
		}
	};
	this.start = function(){
		if(!this._http){
		//this._http = runtime._ajax._http;  //[TODO]没有使用队列机制之前，不可以复用xhr组件
			this._http = runtime._ajax.getXmlHttpObject();
		}
		var xhr = this._http;
		var method = this._data.method;
		runtime.log("[Request::start]" + this._url);
		xhr.open(method, this._url, true);
		if(method == "POST"){
			xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		}
		if(this._headers){
			for(var k in this._headers){
				xhr.setRequestHeader(k, this._headers[k]);
			}
		}
		if(!runtime.ie && this._mimetype){
			xhr.overrideMimeType(this._mimetype);
		}else if(this._data.type == "base64"){
			if("mozResponseType" in xhr){
				xhr.mozResponseType = "arraybuffer";
			}else if("responseType" in xhr){
				xhr.responseType = "arraybuffer";
			}else{
				xhr.overrideMimeType("text/plain; charset=x-user-defined");
			}
		}
		//[TODO]需要考虑请求超时问题
		this._timer = runtime.startTimer(runtime._ajax._timeout, this, function(){
			this.abort();  //中断
			this.send({
				"code": -1001,
				"msg" : "xhr invoke timeout",
				"data": null
			});
		});
		var _this = this;
		xhr.onreadystatechange = function(){_this.onReady();};
		xhr.send(this._query);
	};
	this.abort = function(hold){
		if(this._status == "abort") return;
		runtime.log("[Request::abort]");
		this._status = "abort";
		runtime.stopTimer(this._timer);
		this._timer = 0;
		this._http.abort();
		if(hold){
			runtime._task.hold(this._tid);  //保留任务
		}else{
			runtime._task.cancel(this._tid);  //取消任务
		}
	};
	/**
	 * [TODO]目前只有xhr模式支持retry方法
	 */
	this.retry = function(){
		runtime.log("[Request::retry]");
		switch(this._mode){
		case "xhr":
			this._status = "retry";  //进入重试状态
			runtime._task.hold(this._tid);  //保留任务
			this.preprocess();
			this.start();
			break;
		}
	};
	this.send = function(data){
		runtime._task.send(this._tid, data);
		runtime._task.checkThread();
	};
});